// console.log(owl)

// let t = owl.xml`<t t-if="props.readonly">
//             <span t-esc="props.value or ''"/>
//         </t>
//         <t t-else="">
//             <div t-ref="div">
//                 <textarea class="o_input" t-att-class="{'o_field_translate': props.isTranslatable}" t-att-id="props.id" t-att-placeholder="props.placeholder" t-att-rows="rowCount" t-on-input="onInput" t-ref="textarea" t-on-blur="onBlur"/>
//             </div>
//             <t t-if="props.isTranslatable">
//                 <div>translate</div>
//             </t>
//         </t>`;

// console.log(t);

// let tpl = new owl.TemplateSet();

// let fn = tpl.getTemplate(t);
// console.log(fn);
//owl.compile(t, {props:{readonly:true}})


owl._clz = {};
const isObj = (obj) => toString.call(obj) === "[object Object]";
const utils = {    
    extend() {
        var options, name, src, copy, copyIsArray, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;
        if (typeof target === "boolean") {
            deep = target;
            target = arguments[i] || {};
            i++;
        }
        if (typeof target !== "object" && !isFunction(target)) {
            target = {};
        }
        if (i === length) {
            target = this;
            i--;
        }
        for (; i < length; i++) {
            if ((options = arguments[i]) != null) {
                for (name in options) {
                    copy = options[name];
                    if (name === "__proto__" || target === copy) {
                        continue;
                    }
                    if (deep && copy && (!isObj(copy) || (copyIsArray = Array.isArray(copy)))) {
                        src = target[name];
                        if (copyIsArray && !Array.isArray(src)) {
                            clone = [];
                        } else if (!copyIsArray && !isObj(src)) {
                            clone = {};
                        } else {
                            clone = src;
                        }
                        copyIsArray = false;
                        target[name] = this.extend(deep, clone, copy);
                    } else if (copy !== undefined) {
                        target[name] = copy;
                    }
                }
            }
        }
        return target;
    },
    apply() {
        let options;
        let name;
        let src;
        let copy;
        let copyIsArray;
        let clone;
        let target = arguments[0] || {};
        let i = 1;
        const length = arguments.length;
        let deep = false;
        if (typeof target === 'boolean') {
            deep = target;
            target = arguments[1] || {};
            i = 2;
        }
        if (typeof target !== 'object' && typeof target !== 'function') {
            target = {};
        }
        if (length === i) {
            target = this;
            --i;
        }
        for (; i < length; i++) {
            if ((options = arguments[i]) != null) {
                for (name in options) {
                    if (name && name[0] === '$') {
                        continue;
                    }
                    src = target[name];
                    copy = options[name];
                    if (target === copy) {
                        continue;
                    }
                    if (deep && copy && (isObj(copy) || (copyIsArray = Array.isArray(copy)))) {
                        if (copyIsArray) {
                            copyIsArray = false;
                            clone = src && Array.isArray(src) ? this.extend(deep, [], src) : [];
                        } else {
                            clone = src && isObj(src) ? this.extend(deep, {}, src) : {};
                        }
                        target[name] = this.extend(deep, clone, copy);
                    } else if (copy !== undefined) {
                        if (copy instanceof Function) {
                            utils.defineMethod(target, name, copy);
                        } else {
                            target[name] = copy;
                        }
                    }
                }
            }
        }
        return target;
    },
    defineMethod: function (owner, name, body) {
        const clone = function (method) {
            let newMethod, prop;
            newMethod = function () {
                return method.apply(this, arguments);
            };
            for (prop in method) {
                if (method.hasOwnProperty(prop)) {
                    newMethod[prop] = method[prop];
                }
            }
            return newMethod;
        };
        if (body.$owner) {
            const origin = body;
            body = clone(body);
            body.$origin = origin;
        }
        owner[name] = body;
        body.$name = name;
        body.$owner = owner.$class;
    }
}
owl.define = function (name, def) {
    const clz = function (props, env, node) {
        this.props = props;
        this.env = env;
        this.__owl__ = node;
    };
    const prop = clz.prototype;
    prop.$class = clz;
    clz.$bases = []; 
    prop.setup = function () { }
    prop.render = function (deep = false) {
        this.__owl__.render(deep === true);
    }
    const addBase = function (b) {
        const base = owl._clz[b];
        if (base) {
            clz.$bases.push(base);
            utils.apply(true, prop, base.prototype);
            if(base.template){
                clz.template = base.template;
            }
        } else {
            throw new Error(name + ' 不能扩展未定义的 ' + b);
        }
    };
    if (typeof def.extends === 'string') {
        addBase(def.extends);
    } else if (Array.isArray(def.extends)) {
        for (let i = def.extends.length - 1; i >= 0; i--) {
            const b = def.extends[i];
            if (typeof b === 'string') {
                addBase(b);
            }
        }
    }   
    if(def.template){
        clz.template = def.template;
        delete def.template;
    }
    utils.apply(prop, def);
    prop.callSuper = function () {
        if (!this.$super) {
            this.$super = {};
        }
        const stack = new Error().stack.split('\n')[2].trim().split(/\s+/)[1].trim().split('.');
        const name = stack[stack.length - 1];
        const bases = this.$super[name]?.$bases || this.$class.$bases;
        for (let i = bases.length - 1; i >= 0; i--) {
            let baseMethod = bases[i].prototype[name];
            if (baseMethod) {
                const sp = this.$super[name];
                this.$super[name] = bases[i];
                if (baseMethod.$origin) {
                    this.$super[name] = baseMethod.$origin.$owner;
                    baseMethod = baseMethod.$origin;
                }
                const result = baseMethod.apply(this, arguments);
                this.$super[name] = sp;
                return result;
            }
        }
    };
    clz.$name = name;
    owl._clz[name] = clz;
    return clz;
}

owl.define('Bt', {
    template: owl.xml`<span> bt </span>`,
    setup() {
        console.log('Bt');
    }
});
owl.define('test', {
    template: owl.xml`<t t-if="props.visible">
    <Bt></Bt>
    </t>
    <t t-else="">
    b
    </t>`,
    components: {
       Bt: owl._clz['Bt']
    },
    setup() {
        console.log('run1');
    }
});
owl.define('test', {
    extends: 'test',
    //template: owl.xml`<span> b </span>`,
    setup() {
        this.callSuper(...arguments);
        console.log('run2');
    }
});

const app = new owl.App(owl._clz['test'], { props: {visible:true} });
app.mount(document.body);

// class Component {
//     constructor(props, env, node) {
//         this.props = props;
//         this.env = env;
//         this.__owl__ = node;
//     }
//     setup() { }
//     render(deep = false) {
//         this.__owl__.render(deep === true);
//     }
// }